iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0
Modern Web

Phoenix 1.7 完全教學系列 第 6

6 快速上手夠用的 Elixir (常用慣例與寫法)

  • 分享至 

  • xImage
  •  

Pipe operator (|>)

在 Pipe operator 之前,我們先複習一下函式呼叫

假如我們得到一個使用者輸入的 params,
長這樣

%{"input" => %{"name" => " jack"}}

但我們想要從這個結構拿出 name 的值,
去掉不小心輸入的前後空白,
並且把第一個字大寫

params = %{"input" => %{"name" => " jack"}}

input = Map.get(params, "input")
raw_name = Map.get(input, "name")
trimed_name = String.trim(raw_name)
capitalized_name = String.capitalize(trimed_name)

capitalized_name
#=> "Jack"

但是這樣子寫實在是不好閱讀,也增加了很多個暫時的變數

String.capitalize(String.trim(Map.get(Map.get(params, "input"), "name")))

這樣子雖然省掉中間暫時的變數,但是更難讀了

Elixir 與一些 Functional 語言都有了這個情形提供了 Pipe operator |>

使用 |> 後:

params
|> Map.get("input")
|> Map.get("name")
|> String.trim()
|> String.capitalize()

|> 的作用是,把前面值當成後面的第一個參數
例如

params |> Map.get("input")

就等於

Map.get(params, "input")

我們把全部的步驟使用 |> 串起來後,
程式碼會變得很白話很好閱讀,就像步驟一樣一行一行的寫下去。

每次寫這種一連串的處理我都會想到洋芋片工廠的生產線,或是 Subway 的點餐

https://ithelp.ithome.com.tw/upload/images/20230921/20141054oXDW1HuwEs.png

最後面是 ? 的函式

例如 Enum.any?/1

在 Elixir 裡,函式名稱最後可以是 ?
在慣例裡,如果一個函式以問號結尾,通常他的回傳值都是 true 或是 false

最後面是 ! 的函式

函式名稱最後也可以是 !
通常這種方法都會有 加! 版本與不加的版本,
例如 File.readFile.read!
沒有加上!File.read
在呼叫後會回傳在 Elixir 非常常見的成功或失敗 tuple 組合

File.read("README.md")
#=> {:ok, "內容"}

File.read("No_such_file.txt")
#=> {:error, :enoent}

通常我們會用 case 把他的結果接起來,然後依照回覆的狀況處理,例如

case File.read("README.md") do
  {:ok, content} -> 
    # 處理我們拿到內容後要做的事,這邊用一個虛構的 display_content/1 函式示意
    display_content(content) 
  {:error, reason} -> 
    # 處理錯誤,可能是記錄在系統或是回饋給使用者,這邊用一個虛構的 send_warning/1 函式示意
    send_warning(reason)
end

如果確定這個呼叫通常會正確,在這邊寫正常不會使用的錯誤有點浪費時間
我們可以使用有 ! 版本的函式,
如果呼叫成功他直接回傳內容,如果失敗則是直接 raise 錯誤,交由上一層的錯誤機制處理
(如:Phoenix 會在網頁回覆錯誤頁面或是重新整理)
(後面如果有篇幅,我們可以探索一下 Elixir 著名的錯誤容許機制與 process 管理練,但是現在先不用擔心)

File.read!("README.md")
|> display_content() 

# 可以不用處理錯誤,直接交給下一步使用

稍微困難的測驗

建立一個內有 滷肉飯:100\n炸牛丼飯:160\n炸蝦丼飯:150\n炸雞:70 的 menu.txt 檔案
可以使用這個 script 在終端機產生

echo "滷肉飯:100\n炸牛丼飯:160\n炸蝦丼飯:150\n炸雞:70" >> menu.txt

寫一個函式,讀取 menu.txt 檔案中的文字
並將其轉成我們比較好用的 map

Menu.get("menu.txt")
#=>
%{
  "滷肉飯" => 100,
  "炸牛丼飯" => 160,
  "炸蝦丼飯" => 150,
  "炸雞" => 70
}

提示:先把要做的步驟寫下來,再去找相對應的函式
這邊比較難找的應該是要把目前處理到一半的資料轉成 Map
可以使用 Enum.into/3,
但其實解法很多,這邊先熟悉作法為主,可以先不擔心效率

在實作的時候可以在 iex 試試看
(小祕技:在 iex 裡 v() 會是上一行的結果, 可以執行 h v 來看 v 函式有什麼功能)

參考解答

defmodule Menu do
  def get(path) do
    path
    |> File.read!()
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split(&1, ":"))
    |> Enum.into(%{}, fn [k, v] -> {k, String.to_integer(v)} end)
  end
end

上一篇
5 快速上手夠用的 Elixir (條件判斷)
下一篇
7 Phoenix 專案結構
系列文
Phoenix 1.7 完全教學30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言